﻿// -----------------------------------------------------------------------
// <copyright file="EmptyCollection.cs" company="">
//      Copyright 2012 Samuel Englard
//      
//      Microsoft Reciprocal License (Ms-RL)
// 
//      This license governs use of the accompanying software. If you use the software, you accept this license. If you do 
//      not accept the license, do not use the software.
// 
//      1. Definitions
// 
//      The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under 
//      U.S. copyright law.
// 
//      A "contribution" is the original software, or any additions or changes to the software.
// 
//      A "contributor" is any person that distributes its contribution under this license.
// 
//      "Licensed patents" are a contributor's patent claims that read directly on its contribution.
// 
//      2. Grant of Rights
// 
//      (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in 
//      section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its 
//      contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works 
//      that you create.
// 
//      (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 
//      3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, 
//      have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or 
//      derivative works of the contribution in the software.
// 
//      3. Conditions and Limitations
// 
//      (A) Reciprocal Grants- For any file you distribute that contains code from the software (in source code or binary 
//      format), you must provide recipients the source code to that file along with a copy of this license, which license 
//      will govern that file. You may license other files that are entirely your own work and do not contain code from the
//      software under any terms you choose.
// 
//      (B) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
// 
//      (C) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, 
//      your patent license from such contributor to the software ends automatically.
// 
//      (D) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution 
//      notices that are present in the software.
// 
//      (E) If you distribute any portion of the software in source code form, you may do so only under this license by 
//      including a complete copy of this license with your distribution. If you distribute any portion of the software in 
//      compiled or object code form, you may only do so under a license that complies with this license.
// 
//      (F) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, 
//      guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot 
//      change. To the extent permitted under your local laws, the contributors exclude the implied warranties of 
//      merchantability, fitness for a particular purpose and non-infringement.
//
// </copyright>
// -----------------------------------------------------------------------

using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace System.Collections.ObjectModel
{
    /// <summary>
    ///     A special collection that is empty (useful for returning in errors when you cannot or do not want to return null).
    /// </summary>
    /// <remarks>
    ///     All methods to add items to this "collection" really do nothing, while work with the list act as the there is an empty list.
    /// </remarks>
#if !WINDOWS_PHONE && !SILVERLIGHT
    [Serializable]
#endif
    [ComVisible(false)]
    public class EmptyCollection<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable
    {
        #region IList<T> Members

        /// <summary>
        ///     Searches for the specified object and returns the zero-based index of the first occurrence within the entire <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>. 
        /// </summary>
        /// <param name="item">
        ///     The object to locate in the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>. The value can be <c>null</c> for reference types. 
        /// </param>
        /// <returns>
        ///     The zero-based index of the first occurrence of <paramref name="item"/> within the entire <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>, if found; otherwise, -1. 
        /// </returns>
        public int IndexOf(T item)
        {
            return -1;
        }

        /// <summary>
        ///     Inserts an element into the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/> at the specified index. 
        /// </summary>
        /// <param name="index">
        ///     The zero-based index at which <paramref name="item"/> should be inserted. 
        /// </param>
        /// <param name="item">
        ///     The object to insert. The value can be <c>null</c> for reference types. 
        /// </param>
        public void Insert(int index, T item)
        {
        }

        /// <summary>
        ///     Removes the element at the specified index of the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>. 
        /// </summary>
        /// <param name="index">
        ///     The zero-based index of the element to remove.
        /// </param>
        public void RemoveAt(int index)
        {
        }

        /// <summary>
        ///     Gets or sets the element at the specified index.
        /// </summary>
        /// <param name="index">
        ///     The zero-based index of the element to get or set.
        /// </param>
        /// <returns>
        ///     The element at the specified index.
        /// </returns>
        /// <exception cref="System.ArgumentOutOfRangeException">
        ///     <paramref name="index"/> is less than zero.-or-<paramref name="index"/> is equal to or greater than <see cref="System.Collections.ObjectModel.EmptyCollection{T}.Count"/>.
        /// </exception>
        /// <remarks>
        ///     <para>
        ///         <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/> accepts <c>null</c> as a valid value for reference types and allows duplicate elements. 
        ///     </para>
        ///     <para>
        ///         This property provides the ability to access a specific element in the collection by using the following syntax: <c>myCollection[index]</c>. 
        ///     </para>
        ///     <para>
        ///         The C# language uses the <see cref="this"/> keyword to define the indexers instead of implementing the <c>Item</c> property. Visual Basic implements <c>Item</c> as a default property, which provides the same indexing functionality. 
        ///     </para>
        ///     <para>
        ///         Retrieving the value of this property is an O(1) operation; setting the property is also an O(1) operation.
        ///     </para>
        /// </remarks>
        public T this[int index]
        {
            get
            {
#if !WINDOWS_PHONE
                throw new ArgumentOutOfRangeException("index", index, "index is less than zero.-or-index is equal to or greater than Count.");
#else
                throw new ArgumentOutOfRangeException("index", "index is less than zero.-or-index is equal to or greater than Count.");
#endif
            }
            set
            {
            }
        }

        #endregion

        #region ICollection<T> Members

        /// <summary>
        ///     Adds an object to the end of the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>.
        /// </summary>
        /// <param name="item">
        ///     The object to be added to the end of the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>. The value can be <c>null</c> for reference types. 
        /// </param>
        /// <remarks>
        ///     <para>
        ///         <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/> accepts <c>null</c> as a valid value for reference types and allows duplicate elements. 
        ///     </para>
        ///     <para>
        ///         This method is an O(1) operation.
        ///     </para>
        /// </remarks>
        public void Add(T item)
        {
        }

        /// <summary>
        ///     Removes all elements from the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>.
        /// </summary>
        /// <remarks>
        ///     <see cref="System.Collections.ObjectModel.EmptyCollection{T}.Count"/> is set to zero, and references to other objects from elements of the collection are also released. 
        /// </remarks>
        public void Clear()
        {
        }

        /// <summary>
        ///     Determines whether an element is in the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>.
        /// </summary>
        /// <param name="item">
        ///     The object to locate in the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>. The value can be <c>null</c> for reference types. 
        /// </param>
        /// <returns>
        ///     <c>true</c> if item is found in the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>; otherwise, <c>false</c>. 
        /// </returns>
        public bool Contains(T item)
        {
            return false;
        }

        /// <summary>
        ///     Copies the entire <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/> to a compatible one-dimensional <see cref="System.Array"/>, starting at the specified index of the target array. 
        /// </summary>
        /// <param name="array">
        ///     The one-dimensional <see cref="System.Array"/> that is the destination of the elements copied from <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>. The <see cref="System.Array"/> must have zero-based indexing. 
        /// </param>
        /// <param name="arrayIndex">
        ///     The zero-based index in <paramref name="array"/> at which copying begins. 
        /// </param>
        public void CopyTo(T[] array, int arrayIndex)
        {
        }

        /// <summary>
        ///     Gets the number of elements actually contained in the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>.
        /// </summary>
        /// <value>
        ///     The number of elements actually contained in the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>.
        /// </value>
        /// <remarks>
        ///     Retrieving the value of this property is an O(1) operation.
        /// </remarks>
        public int Count
        {
            get 
            {
                return 0;
            }
        }

        /// <summary>
        ///     Gets a value indicating whether the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/> is read-only. 
        /// </summary>
        public bool IsReadOnly
        {
            get 
            {
                return true;
            }
        }

        /// <summary>
        ///     Removes the first occurrence of a specific object from the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>. 
        /// </summary>
        /// <param name="item">
        ///     The object to remove from the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>.  The value can be <c>null</c> for reference types. 
        /// </param>
        /// <returns>
        ///     <c>true</c> if <paramref name="item"/> is successfully removed; otherwise, <c>false</c>. This method also returns <c>false</c> if <paramref name="item"/> was not found in the original <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>. 
        /// </returns>
        public bool Remove(T item)
        {
            return false;
        }

        #endregion

        #region IEnumerable<T> Members

        /// <summary>
        ///     Returns an enumerator that iterates through the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>. 
        /// </summary>
        /// <returns>
        ///     An <see cref="System.Collections.Generic.IEnumerator{T}"/> for the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>. 
        /// </returns>
        public IEnumerator<T> GetEnumerator()
        {
            // Cheap way to create an empty enumerator
            while (false)
            {
                // Yes this method is unreachable but that's the point.By putting an unreachable yield an empty enumerator is created.
                yield return default(T);
            }
        }

        #endregion

        #region IEnumerable Members

        /// <summary>
        ///     Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        ///     An <see cref="System.Collections.IEnumerator"/> that can be used to iterate through the collection. 
        /// </returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            // Cheap way to create an empty enumerator
            while (false)
            {
                // Yes this method is unreachable but that's the point.By putting an unreachable yield an empty enumerator is created.
                yield return null;
            }
        }

        #endregion

        #region IList Members

        /// <summary>
        ///     Adds an item to the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>. 
        /// </summary>
        /// <param name="value">
        ///     The <see cref="System.Object"/> to add to the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>. 
        /// </param>
        /// <returns>
        ///     The position into which the new element was inserted.
        /// </returns>
        public int Add(object value)
        {
            return -1;
        }

        /// <summary>
        ///     Determines whether the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/> contains a specific value. 
        /// </summary>
        /// <param name="value">
        ///     The <see cref="System.Object"/> to locate in the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>. 
        /// </param>
        /// <returns>
        ///     <c>true</c> if the <see cref="System.Object"/> is found in the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>; otherwise, <c>false</c>.
        /// </returns>
        public bool Contains(object value)
        {
            return false;
        }

        /// <summary>
        ///     Determines the index of a specific item in the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>. 
        /// </summary>
        /// <param name="value">
        ///     The <see cref="System.Object"/> to locate in the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>.
        /// </param>
        /// <returns>
        ///     The index of <paramref name="value"/> if found in the list; otherwise, -1. 
        /// </returns>
        public int IndexOf(object value)
        {
            return -1;
        }

        /// <summary>
        ///     Inserts an item into the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/> at the specified index. 
        /// </summary>
        /// <param name="index">
        ///     The zero-based index at which <paramref name="value"/> should be inserted. 
        /// </param>
        /// <param name="value">
        ///     The <see cref="System.Object"/> to insert into the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>. 
        /// </param>
        public void Insert(int index, object value)
        {
        }

        /// <summary>
        ///     Gets a value indicating whether the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/> has a fixed size. 
        /// </summary>
        public bool IsFixedSize
        {
            get 
            {
                return true;
            }
        }

        /// <summary>
        ///     Removes the first occurrence of a specific object from the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>. 
        /// </summary>
        /// <param name="value">
        ///     The <see cref="System.Object"/> to remove from the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>. 
        /// </param>
        public void Remove(object value)
        {
        }

        /// <summary>
        ///     Gets or sets the element at the specified index.
        /// </summary>
        /// <param name="index">
        ///     The zero-based index of the element to get or set.
        /// </param>
        /// <returns>
        ///     The element at the specified index.
        /// </returns>
        object IList.this[int index]
        {
            get
            {
#if !WINDOWS_PHONE
                throw new ArgumentOutOfRangeException("index", index, "index is less than zero.-or-index is equal to or greater than Count.");
#else
                throw new ArgumentOutOfRangeException("index", "index is less than zero.-or-index is equal to or greater than Count.");
#endif
            }
            set
            {
            }
        }

        #endregion

        #region ICollection Members

        /// <summary>
        ///     Copies the elements of the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/> to an <see cref="System.Array"/>, starting at a particular <see cref="System.Array"/> index. 
        /// </summary>
        /// <param name="array">
        ///     The one-dimensional <see cref="System.Array"/> that is the destination of the elements copied from <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>. The <see cref="System.Array"/> must have zero-based indexing. 
        /// </param>
        /// <param name="index">
        ///     The zero-based index in <paramref name="array"/> at which copying begins. 
        /// </param>
        public void CopyTo(Array array, int index)
        {
        }

        /// <summary>
        ///     Gets a value indicating whether access to the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/> is synchronized (thread safe). 
        /// </summary>
        public bool IsSynchronized
        {
            get 
            {
                return true;
            }
        }

        /// <summary>
        ///     Gets an object that can be used to synchronize access to the <see cref="System.Collections.ObjectModel.EmptyCollection{T}"/>. 
        /// </summary>
        public object SyncRoot
        {
            get 
            {
                return this;
            }
        }

        #endregion

        #region Signleton-ness

        /*
         * Since all instances of this class behave the same way I've added 
         * this way to use it like a singleton. The advantage is that it is 
         * only "newed" once. The downside is that even if you never need it 
         * again there will be one in memory (though the footprint is small).
         * 
         * NOTE: The singleton is on a per-Type bases.
         */

        /// <summary>
        ///     The single <<see cref="System.Collections.ObjectModel.EmptyCollection{T}"/> to have.
        /// </summary>
        private static EmptyCollection<T> singleton;

        /// <summary>
        ///     The single <<see cref="System.Collections.ObjectModel.EmptyCollection{T}"/> to have.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] // Normally I'd agree with this but this is a weird class to start with so some things won't be "right".
        public static EmptyCollection<T> Singleton
        {
            get
            {
                if (singleton == null)
                {
                    singleton = new EmptyCollection<T>();
                }
                return singleton;
            }
        }

        #endregion
    }
}
